package com.ihr360.payroll.service.salarytasklet.step.processor;

import com.ihr360.commons.data.util.Pair;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.Ihr360NumberUtils;
import com.ihr360.commons.model.type.EnrollAdjustFormula;
import com.ihr360.commons.model.type.FormulaFrom;
import com.ihr360.commons.model.type.WorkHourType;
import com.ihr360.expr.ExprDouble;
import com.ihr360.expr.ExprException;
import com.ihr360.expr.engine.DependencyEngine;
import com.ihr360.expr.engine.EngineProvider;
import com.ihr360.expr.parser.ExprToken;
import com.ihr360.job.core.annotation.BeforeProcess;
import com.ihr360.job.core.item.ItemProcessor;
import com.ihr360.job.core.item.support.OneShotItemWriter;
import com.ihr360.job.core.repository.JobRepository;
import com.ihr360.job.core.scope.context.SimpleStepContext;
import com.ihr360.payroll.model.base.IAttendanceRuleWorkHourConfig;
import com.ihr360.payroll.model.base.IBaseAttendanceRule;
import com.ihr360.payroll.model.dto.attendance.plan.AttendancePlan;
import com.ihr360.payroll.model.dto.attendance.rule.SalaryAdjustWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.enroll.EnrollAdjustRule;
import com.ihr360.payroll.model.dto.attendance.rule.enroll.EnrollAdjustRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.overtime.OvertimeRule;
import com.ihr360.payroll.model.dto.attendance.rule.overtime.OvertimeRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.overtime.SyntheticalOverTimeRule;
import com.ihr360.payroll.model.dto.attendance.rule.personalleave.PersonalLeaveRule;
import com.ihr360.payroll.model.dto.attendance.rule.personalleave.PersonalLeaveRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.attendance.rule.sickleave.DeductionByType;
import com.ihr360.payroll.model.dto.attendance.rule.sickleave.SickLeaveRule;
import com.ihr360.payroll.model.dto.attendance.rule.sickleave.SickLeaveRuleWorkHourConfig;
import com.ihr360.payroll.model.dto.salary.embeddable.SalaryFieldValue;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.meta.SalaryFieldCode;
import com.ihr360.payroll.service.alps.profile.EnrollDismissAdjustServiceProfile;
import com.ihr360.payroll.service.alps.profile.OverTimeRuleServiceProfile;
import com.ihr360.payroll.service.alps.profile.PersonLeaveServiceProfile;
import com.ihr360.payroll.service.alps.profile.SalaryAdjustServiceProfile;
import com.ihr360.payroll.service.alps.profile.SickLeaveServiceProfile;
import com.ihr360.payroll.service.alps.profile.provider.ProfileParamProvider;
import com.ihr360.payroll.service.salarytasklet.dao.AttendancePlanDao;
import com.ihr360.payroll.service.salarytasklet.expr.engine.SalaryDependencyEngine;
import com.ihr360.payroll.service.salarytasklet.expr.engine.SalaryExprEngineProvider;
import com.ihr360.payroll.service.salarytasklet.step.SalaryCalcContext;
import com.ihr360.payroll.type.BasementType;
import com.ihr360.sharedservice.model.WorkingCalendar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @author davidwei
 */
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AttendancePlanDependencyProcessor implements ItemProcessor<SalaryCalcContext, SalaryDependencyEngine> {


    /**
     * 1. 工龄或司龄 2. 工龄或司龄区间 3. 工龄或区间所对应的扣除比例
     */
    private static final String SICK_LEAVE_DUCATION_RATIO_TMPL = "HLOOKUP(%s,{%s;%s},2)";


//    "=HLOOKUP(" +
//            "工龄或司龄,{" +
//            "年限;" +
//            "HLOOKUP(休假时间,{0,2,4,5,8;0.8,0.6,0.5,0.4,0},2)," +
//            "HLOOKUP(休假时间,{0,2,4,5,8;0.7,0.5,0.4,0.3,0},2)," +
//            "HLOOKUP(休假时间,{0,2,4,5,8;0.6,0.4,0.3,0.2,0},2)," +
//            "HLOOKUP(休假时间,{0,2,4,5,8;0.5,0.3,0.2,0.1,0},2)," +
//            "HLOOKUP(休假时间,{0,2,4,5,8;0.4,0.2,0.1,0,0},2)" +
//            "},2)";

    private static final int NUMBER_SCALE = 2;
    private static final int RATIO_SCALE = 2;

    final Logger logger = LoggerFactory.getLogger(DependencyEngine.class.getName());
    private SimpleStepContext simpleStepContext;

    private JobRepository jobRepository;

    private SalaryDependencyEngine engine;

    private AttendancePlanDao attendancePlanDao;

    private boolean standardBeOnDuty;
    private boolean syntheticalBeOnDuty;
    private int supposedWorkingDays = -1;

    @Override
    public SalaryDependencyEngine process(SalaryCalcContext calcContext) throws Exception {
        engine = calcContext.dependencyEngineHolder.getWrittenItem();
        if (engine == null) {
            engine = new SalaryDependencyEngine(new SalaryExprEngineProvider(calcContext.variableFetcher));
        }

        EngineProvider engineProvider = engine.getProvider();

        engine.setAutoCalculate(false);

        AttendancePlan attendancePlan = attendancePlanDao.findOne(calcContext.attendancePlanId);
        if (attendancePlan == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪方案]", "公司未设置考勤计薪方案");
            return engine;
        }

        WorkHourType workHourType = attendancePlan.getWorkHourType();

        standardBeOnDuty = attendancePlan.isStandardBeOnDuty();
        syntheticalBeOnDuty = attendancePlan.isSyntheticalBeOnDuty();

        if (standardBeOnDuty || syntheticalBeOnDuty) {
            OneShotItemWriter<WorkingCalendar> workingCalendarHolder = calcContext.workingCalendarHolder;
            WorkingCalendar calendar = workingCalendarHolder.getWrittenItem();
            supposedWorkingDays = calendar.getWorkingDaysBetween(calcContext.payrollPeriodRange.getStart(), calcContext.payrollPeriodRange.getEnd());

            if (standardBeOnDuty) {
                jobRepository.info(simpleStepContext, "[考勤计薪规则]", String.format("标准工时 月计薪天数=实际应出勤天数(%s) 已开启", supposedWorkingDays));
            }
            if (syntheticalBeOnDuty) {
                jobRepository.info(simpleStepContext, "[考勤计薪规则]", String.format("综合工时 月计薪天数=实际应出勤天数(%s) 已开启", supposedWorkingDays));
            }
        }


        //加班工资规则
        OvertimeRuleWorkHourConfig overtimeRuleWorkHourConfig = attendancePlan.getOvertimeConfig();
        OvertimeRule overtimeRule = attendancePlan.getOvertimeRule();

        if (overtimeRuleWorkHourConfig == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "加班计薪规则工时未设置");
        }


        if (overtimeRule == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "加班计薪规则未设置");
        } else {
            if (WorkHourType.STANDARD.equals(workHourType)) {
                processStandardOverTimeRule(engineProvider, overtimeRule, overtimeRuleWorkHourConfig);
            } else if (WorkHourType.SYNTHETICAL.equals(workHourType)) {
                processSyntheticalOverTimeRule(engineProvider, overtimeRule, overtimeRuleWorkHourConfig);
            } else if (WorkHourType.ALL.equals(workHourType)) {
                processStandardOverTimeRule(engineProvider, overtimeRule, overtimeRuleWorkHourConfig);
                processSyntheticalOverTimeRule(engineProvider, overtimeRule, overtimeRuleWorkHourConfig);
            }
        }

        //病假扣款规则
        SickLeaveRuleWorkHourConfig sickLeaveRuleWorkHourConfig = attendancePlan.getSickLeaveConfig();
        SickLeaveRule sickLeaveRule = attendancePlan.getSickLeaveRule();
        if (sickLeaveRuleWorkHourConfig == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "病假扣款规则工时未设置");
        }
        if (sickLeaveRule == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "病假扣款规则未设置");
        } else {
            if (sickLeaveRule.getEnableSubsectionPaid()) {
                calcContext.sickLeavePhasedCalcEnable = true;
                jobRepository.info(simpleStepContext, "[考勤计薪规则]", "病假扣款分段计算已开启");
            } else {
                calcContext.sickLeavePhasedCalcEnable = false;
                jobRepository.info(simpleStepContext, "[考勤计薪规则]", "病假扣款分段计算已关闭");
            }
            processBaseAttendanceRule(engineProvider, sickLeaveRule, WorkHourType.STANDARD, SickLeaveServiceProfile.profileParamProvider);
            processLaborHourConfig(engineProvider, sickLeaveRuleWorkHourConfig, WorkHourType.STANDARD, SickLeaveServiceProfile.profileParamProvider);

            //设置扣款公式
            DeductionByType deductionBy = sickLeaveRule.getDeductionBy();
            Boolean advancedDeductionEnable = sickLeaveRule.getAdvancedDeductionEnable();
            SickLeaveRule.DeductConfigWrapper configWrapper = sickLeaveRule.getDeductConfig();
            if (deductionBy != null && Boolean.TRUE.equals(advancedDeductionEnable)) {
                SickLeaveRule.DeductConfigWrapper deductConfig = configWrapper;
                if (deductConfig != null) {
                    Integer[] years = deductConfig.getYears();
                    SickLeaveRule.DeductConfigWrapper.Config[] configs = deductConfig.getConfigs();
                    if (CollectionUtils.isNotEmpty(years) && CollectionUtils.isNotEmpty(configs)) {
                        if (years.length != configs.length) {
                            jobRepository.error(simpleStepContext, "[病假扣款比例]", "时间间隔个数和扣款配置个数不匹配");
                        } else {
                            ProfileParamProvider paramProvider = SickLeaveServiceProfile.profileParamProvider;
                            List<String> stringYears = Ihr360NumberUtils.toList(years);
                            //工龄或者司龄区间值
                            String serviceOrWorkingYears = String.join(",", stringYears);
                            List<String> hlookups = new ArrayList<>();
                            for (SickLeaveRule.DeductConfigWrapper.Config config : configs) {
                                Integer[] days = config.getDays();
                                Integer[] ratios = config.getRatios();
                                List<String> daysStrArray = Ihr360NumberUtils.toList(days);
                                String daysStr = String.join(",", daysStrArray);
                                List<String> ratiosStrArray = new ArrayList<>(ratios.length);
                                for (Integer ratio : ratios) {
                                    double v = Ihr360NumberUtils.fakeDouble2Double(ratio, RATIO_SCALE);
                                    ratiosStrArray.add(String.valueOf(v));
                                }

                                String ratioStr = String.join(",", ratiosStrArray);

                                String hlookup = String.format(SICK_LEAVE_DUCATION_RATIO_TMPL, SalaryFieldCode.SICK_LEAVE_CNT, daysStr, ratioStr);
                                hlookups.add(hlookup);
                            }
                            String hookupsStr = String.join(",", hlookups);
                            String yearCode = DeductionByType.SERVICE_YEAR.equals(deductionBy) ? SalaryFieldCode.SEREVICE_YEAR : SalaryFieldCode.WORKING_YEARS;

                            String formula = String.format(SICK_LEAVE_DUCATION_RATIO_TMPL, yearCode, serviceOrWorkingYears, hookupsStr);

                            engine.set(paramProvider.buildVariable(workHourType, paramProvider.numOpRatio()), "=" + formula);
                        }
                    }
                }
            }
        }


        //事假扣款规则
        PersonalLeaveRuleWorkHourConfig personalLeaveRuleWorkHourConfig = attendancePlan.getPersonalLeaveConfig();
        PersonalLeaveRule personalLeaveRule = attendancePlan.getPersonalLeaveRule();
        if (personalLeaveRuleWorkHourConfig == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "事假扣款规则工时未设置");
        }
        if (personalLeaveRule == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "事假扣款规则未设置");
        } else {
            if (personalLeaveRule.getEnableSubsectionPaid()) {
                calcContext.personalLeavePhasedCalcEnable = true;
                jobRepository.info(simpleStepContext, "[考勤计薪规则]", "事假扣款分段计算已开启");
            } else {
                calcContext.personalLeavePhasedCalcEnable = false;
                jobRepository.info(simpleStepContext, "[考勤计薪规则]", "事假扣款分段计算已关闭");
            }
            processCalcBase(engineProvider, personalLeaveRule, workHourType, PersonLeaveServiceProfile.profileParamProvider);
            processLaborHourConfig(engineProvider, personalLeaveRuleWorkHourConfig, WorkHourType.STANDARD, PersonLeaveServiceProfile.profileParamProvider);

        }
        //入离职调整规则
        EnrollAdjustRuleWorkHourConfig enrollAdjustRuleWorkHourConfig = attendancePlan.getEnrollAdjustConfig();
        EnrollAdjustRule enrollAdjustRule = attendancePlan.getEnrollAdjustRule();

        if (enrollAdjustRuleWorkHourConfig == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "入离职调整工时未设置");
        }

        if (enrollAdjustRule == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "入离职调整规则未设置");
        } else {
            processCalcBase(engineProvider, enrollAdjustRule, workHourType, EnrollDismissAdjustServiceProfile.profileParamProvider);
            Pair<Double, Double> pair = processLaborHourConfig(engineProvider, enrollAdjustRuleWorkHourConfig, WorkHourType.STANDARD, EnrollDismissAdjustServiceProfile.profileParamProvider);
            processEnrollAdjustFormula(calcContext, enrollAdjustRule.getFormula());
            if (EnrollAdjustFormula.FORMULA3.equals(enrollAdjustRule.getFormula())) {
                //trick fix: 添加离职日期的依赖，为了处理入离职调整
                engine.set("tmpvariable4quitout", "=STAFF_ROSTER$leaveDate");
            }
            calcContext.monthlyEDStandardWorkingDays = pair.getFirst();
        }


        //根据设置处理分段计算公式
        //processPhaseCalcFormula(calcContext);

        //处理调薪调整公式
        SalaryAdjustWorkHourConfig salaryAdjustConfig = attendancePlan.getSalaryAdjustConfig();
        if (salaryAdjustConfig != null) {
            processLaborHourConfig(engineProvider, salaryAdjustConfig, WorkHourType.STANDARD, SalaryAdjustServiceProfile.profileParamProvider);
        }
        return engine;
    }

    /**
     * 根据用户设定，设置入离职调整的公式
     *
     * @param formula
     */
    private void processEnrollAdjustFormula(SalaryCalcContext calcContext, EnrollAdjustFormula formula) {
        SalaryField salaryField = calcContext.salaryFieldsHolder.getWrittenItem().get(SalaryFieldCode.ADJUSTMENT_FOR_ATTENDANCE);
        if (salaryField == null) {
            return;
        }
        if (formula == null) {
            formula = EnrollAdjustFormula.FORMULA1;
        }
        SalaryFieldValue valueMeta = salaryField.getValueMeta();
        if (valueMeta == null) {
            valueMeta = new SalaryFieldValue();
        }
        if (!FormulaFrom.USR.equals(valueMeta.getFormulaFrom())) {
            valueMeta.setFormula(formula.getFormula());
        }
        salaryField.setValueMeta(valueMeta);
        calcContext.enrollAdjustFormula = formula;
        return;
    }

    private void processPhaseCalcFormula(SalaryCalcContext calcContext) {
        if (!calcContext.sickLeavePhasedCalcEnable) {
            SalaryField salaryField = calcContext.salaryFieldsHolder.getWrittenItem().get(SalaryFieldCode.SICK_LEAVE_DEDUCTION);
            if (salaryField != null) {
                SalaryFieldValue valueMeta = salaryField.getValueMeta();
                if (valueMeta != null && valueMeta.getPhasedFormula() != null) {
                    valueMeta.setPhasedFormula(null);
                    salaryField.setValueMeta(valueMeta);
                }
            }
        }
        if (!calcContext.personalLeavePhasedCalcEnable) {
            SalaryField salaryField = calcContext.salaryFieldsHolder.getWrittenItem().get(SalaryFieldCode.UNPAID_LEAVE_DEDUCTION);
            if (salaryField != null) {
                SalaryFieldValue valueMeta = salaryField.getValueMeta();
                if (valueMeta != null && valueMeta.getPhasedFormula() != null) {
                    valueMeta.setPhasedFormula(null);
                    salaryField.setValueMeta(valueMeta);
                }
            }
        }
    }

    private void processStandardOverTimeRule(EngineProvider engineProvider, OvertimeRule rule, OvertimeRuleWorkHourConfig config) {
        //处理公用规则
        processBaseAttendanceRule(engineProvider, rule, WorkHourType.STANDARD, OverTimeRuleServiceProfile.standardParamProvder);
        //处理工时
        processLaborHourConfig(engineProvider, config, WorkHourType.STANDARD, OverTimeRuleServiceProfile.standardParamProvder);
        //处理计算比例
        processCalcRatio(engineProvider, rule, WorkHourType.STANDARD);
    }

    /**
     * 处理工时配置信息
     *
     * @param engineProvider
     * @param config
     * @param workHourType
     * @param profileParamProvider
     */
    private Pair processLaborHourConfig(EngineProvider engineProvider, IAttendanceRuleWorkHourConfig config, WorkHourType workHourType, ProfileParamProvider profileParamProvider) {
        Integer mWorkCount = getMonthlyWorkDays(config, workHourType);

        double mWorkCountD;
        if (mWorkCount == null) {
            mWorkCountD = profileParamProvider.monthlyWorkDays().getDefaultValue();
            jobRepository.warning(simpleStepContext, profileParamProvider.logTitle(),
                    String.format("月标准计薪天数未设定,使用默认值:%f%%", mWorkCountD));

            engineProvider.addVariable(
                    profileParamProvider.buildVariable(workHourType, profileParamProvider.monthlyWorkDays()),
                    new ExprDouble(mWorkCountD));

        } else {
            mWorkCountD = Ihr360NumberUtils.fakeDouble2Double(mWorkCount, NUMBER_SCALE);
            engineProvider.addVariable(
                    profileParamProvider.buildVariable(workHourType, profileParamProvider.monthlyWorkDays()),
                    new ExprDouble(mWorkCountD));
        }

        Integer dWorkCount = getDailyWorkcount(config, workHourType);
        double dWorkCountD;
        if (dWorkCount == null) {
            dWorkCountD = profileParamProvider.monthlyWorkDays().getDefaultValue();
            jobRepository.warning(simpleStepContext, profileParamProvider.logTitle(),
                    String.format("日标准工作小时未设定,使用默认值:%f%%", dWorkCountD));

            engineProvider.addVariable(
                    profileParamProvider.buildVariable(workHourType, profileParamProvider.dailyWorkHours()),
                    new ExprDouble(profileParamProvider.dailyWorkHours().getDefaultValue()));
        } else {
            dWorkCountD = Ihr360NumberUtils.fakeDouble2Double(dWorkCount, NUMBER_SCALE);
            engineProvider.addVariable(
                    profileParamProvider.buildVariable(workHourType, profileParamProvider.dailyWorkHours()),
                    new ExprDouble(dWorkCountD));
        }
        return Pair.of(mWorkCountD, dWorkCountD);
    }

    private Integer getDailyWorkcount(IAttendanceRuleWorkHourConfig config, WorkHourType workHourType) {
        if (config == null) {
            return null;
        }
        return WorkHourType.STANDARD.equals(workHourType) ? config.getDailyWorkCount() : config.getsDailyWorkCount();
    }

    private Integer getMonthlyWorkDays(IAttendanceRuleWorkHourConfig config, WorkHourType workHourType) {
        if (config == null) {
            return null;
        }
        Integer mWorkCount = WorkHourType.STANDARD.equals(workHourType) ? config.getMonthWorkCount() : config.getsMonthWorkCount();

        if (standardBeOnDuty && WorkHourType.STANDARD.equals(workHourType) && supposedWorkingDays != -1) {
            mWorkCount = supposedWorkingDays * 100;
        }
        if (syntheticalBeOnDuty && WorkHourType.SYNTHETICAL.equals(workHourType) && supposedWorkingDays != -1) {
            mWorkCount = supposedWorkingDays * 100;
        }
        return mWorkCount;
    }

    /**
     * 标准工时 基数比例设定
     *
     * @param engineProvider
     * @param rule
     * @param workHourType
     */
    private void processCalcRatio(EngineProvider engineProvider, OvertimeRule rule, WorkHourType workHourType) {
        if (WorkHourType.STANDARD.equals(workHourType)) {
            if (rule == null || rule.getWeekendRatio() == null) {
                jobRepository.warning(simpleStepContext, String.format("[加班工资规则(%s)]", workHourType.getTitle()),
                        String.format("周末比例未设定,使用默认值:%.2f%%", OverTimeRuleServiceProfile.DEFAULT_VAL.WEEKEND_RATIO));
                engineProvider.addVariable(OverTimeRuleServiceProfile.buildVariableId(WorkHourType.STANDARD,
                                OverTimeRuleServiceProfile.weekendRatio),
                        new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(OverTimeRuleServiceProfile.DEFAULT_VAL.WEEKEND_RATIO, NUMBER_SCALE)));
            } else {
                engineProvider.addVariable(OverTimeRuleServiceProfile.buildVariableId(WorkHourType.STANDARD,
                                OverTimeRuleServiceProfile.weekendRatio),
                        new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(rule.getWeekendRatio(), RATIO_SCALE)));
            }
        }

        if (rule == null || rule.getHolidayRatio() == null) {
            jobRepository.warning(simpleStepContext, String.format("[加班工资规则(%s)]", workHourType.getTitle()),
                    String.format("节假日比例未设定,使用默认值:%.2f%%", OverTimeRuleServiceProfile.DEFAULT_VAL.HOLIDAY_RATIO));
            engineProvider.addVariable(OverTimeRuleServiceProfile.buildVariableId(WorkHourType.STANDARD, OverTimeRuleServiceProfile.holidayRatio),
                    new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(OverTimeRuleServiceProfile.DEFAULT_VAL.HOLIDAY_RATIO, NUMBER_SCALE)));
        } else {
            engineProvider.addVariable(OverTimeRuleServiceProfile.buildVariableId(WorkHourType.STANDARD, OverTimeRuleServiceProfile.holidayRatio),
                    new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(rule.getHolidayRatio(), RATIO_SCALE)));
        }
    }

    /**
     * 综合工时计算比例设定
     *
     * @param engineProvider
     * @param rule
     * @param workHourType
     */
    private void processCalcRatio(EngineProvider engineProvider, SyntheticalOverTimeRule rule, WorkHourType workHourType) {
        if (rule == null || rule.getHolidayRatio() == null) {
            jobRepository.warning(simpleStepContext, String.format("[加班工资规则(%s)]", workHourType.getTitle()),
                    String.format("节假日比例未设定,使用默认值:%.2f%%", OverTimeRuleServiceProfile.DEFAULT_VAL.HOLIDAY_RATIO));
            engineProvider.addVariable(OverTimeRuleServiceProfile.buildVariableId(WorkHourType.STANDARD, OverTimeRuleServiceProfile.holidayRatio),
                    new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(OverTimeRuleServiceProfile.DEFAULT_VAL.HOLIDAY_RATIO, NUMBER_SCALE)));
        } else {
            engineProvider.addVariable(OverTimeRuleServiceProfile.buildVariableId(WorkHourType.STANDARD, OverTimeRuleServiceProfile.holidayRatio),
                    new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(rule.getHolidayRatio(), RATIO_SCALE)));
        }
    }


    /**
     * 标准工时和综合工时公用部分
     *
     * @param engineProvider
     * @param rule
     * @param workHourType
     * @param paramProvider
     */
    private void processBaseAttendanceRule(EngineProvider engineProvider, IBaseAttendanceRule rule, WorkHourType workHourType, ProfileParamProvider paramProvider) {
        if (rule == null || rule.getNumOpRatio() == null) {
            double defaultNumOpRatio = paramProvider.numOpRatio().getDefaultValue();
            jobRepository.warning(simpleStepContext, paramProvider.logTitle(),
                    String.format("平日比例未设定,使用默认值:%.2f%%", defaultNumOpRatio));
            engineProvider.addVariable(paramProvider.buildVariable(workHourType, paramProvider.numOpRatio()),
                    new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(defaultNumOpRatio, NUMBER_SCALE)));
        } else {
            engineProvider.addVariable(paramProvider.buildVariable(workHourType, paramProvider.numOpRatio()),
                    new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(rule.getNumOpRatio(), RATIO_SCALE)));
        }

        processCalcBase(engineProvider, rule, workHourType, paramProvider);
    }

    /**
     * 处理工资基数和基数比例
     *
     * @param engineProvider
     * @param rule
     * @param workHourType
     * @param paramProvider
     */
    private void processCalcBase(EngineProvider engineProvider, IBaseAttendanceRule rule, WorkHourType workHourType, ProfileParamProvider paramProvider) {

        if (rule == null) {
            jobRepository.warning(simpleStepContext, paramProvider.logTitle(), "规则未设置");
            return;
        }
        BasementType type = rule.getBasementType();
        if (type == null) {
            jobRepository.warning(simpleStepContext, paramProvider.logTitle(), "无法获取基数类型");
            return;
        }
        if (BasementType.FIXED.equals(type)) {
            if (rule.getFixedBase() == null) {
                jobRepository.warning(simpleStepContext, paramProvider.logTitle(),
                        String.format("固定基数未设定,使用默认值:%.2f%%", paramProvider.calcBase().getDefaultValue()));
                engineProvider.addVariable(
                        paramProvider.buildVariable(workHourType, paramProvider.calcBase()),
                        new ExprDouble(paramProvider.calcBase().getDefaultValue()));
            } else {
                engineProvider.addVariable(
                        paramProvider.buildVariable(workHourType, paramProvider.calcBase()),
                        new ExprDouble(rule.getFixedBase()));
            }

            //固定为100
            engineProvider.addVariable(
                    paramProvider.buildVariable(workHourType, paramProvider.baseRatio()),
                    new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(paramProvider.baseRatio().getDefaultValue(), NUMBER_SCALE)));

        } else if (BasementType.REFERENCE.equals(type)) {

            if (rule == null || rule.getBaseRatio() == null) {
                jobRepository.warning(simpleStepContext, paramProvider.logTitle(),
                        String.format("基数比例未设定,使用默认值:%.2f%%", paramProvider.baseRatio().getDefaultValue()));
                engineProvider.addVariable(
                        paramProvider.buildVariable(workHourType, paramProvider.baseRatio()),
                        new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(paramProvider.baseRatio().getDefaultValue(), NUMBER_SCALE)));
            } else {
                engineProvider.addVariable(
                        paramProvider.buildVariable(workHourType, paramProvider.baseRatio()),
                        new ExprDouble(Ihr360NumberUtils.fakeDouble2Double(rule.getBaseRatio(), RATIO_SCALE)));
            }
            try {
                List<SalaryField> fields = rule.getReferenceFields();
                if (CollectionUtils.isEmpty(fields)) {
                    jobRepository.warning(simpleStepContext, paramProvider.logTitle(),
                            String.format("引用基数配置项目为空,使用默认值:%.2f", paramProvider.calcBase().getDefaultValue()));
                    engineProvider.addVariable(
                            paramProvider.buildVariable(workHourType, paramProvider.calcBase()),
                            new ExprDouble(paramProvider.calcBase().getDefaultValue()));
                } else {
                    StringBuilder formula = new StringBuilder(ExprToken.EQUAL.val);
                    fields.stream().forEach(f -> {
                                formula.append(f.getCode());
                                formula.append(ExprToken.PLUS.val);
                            }
                    );
                    formula.deleteCharAt(formula.length() - 1);
                    engine.set(paramProvider.buildVariable(workHourType, paramProvider.calcBase()), formula.toString());
                }
            } catch (ExprException e) {
                logger.error(e.getMessage(), e);
                jobRepository.error(simpleStepContext, paramProvider.logTitle(),
                        String.format("引用基数配置项目解析异常:%s", e.getMessage()));
            }
        }
    }

    /**
     * 综合工时加班计薪规则
     *
     * @param engineProvider
     * @param rule
     * @param config
     */
    private void processSyntheticalOverTimeRule(EngineProvider engineProvider, OvertimeRule rule, OvertimeRuleWorkHourConfig config) {
        SyntheticalOverTimeRule syntheticalOverTimeRule = rule != null ? rule.getRule() : null;

        if (syntheticalOverTimeRule == null) {
            jobRepository.warning(simpleStepContext, "[考勤计薪规则]", "综合工时加班计薪规则未设置");
        }

        //处理公用规则
        processBaseAttendanceRule(engineProvider, syntheticalOverTimeRule, WorkHourType.SYNTHETICAL, OverTimeRuleServiceProfile.syntheticalParamProvder);
        //处理工时
        processLaborHourConfig(engineProvider, config, WorkHourType.SYNTHETICAL, OverTimeRuleServiceProfile.syntheticalParamProvder);
        //处理计算比例
        processCalcRatio(engineProvider, syntheticalOverTimeRule, WorkHourType.SYNTHETICAL);
    }

    @BeforeProcess
    public void setSimpleStepContext(SalaryCalcContext calcContext, SimpleStepContext simpleStepContext) {
        this.simpleStepContext = simpleStepContext;
    }

    @Autowired
    public void setJobRepository(JobRepository jobRepository) {
        this.jobRepository = jobRepository;
    }

    @Autowired
    public void setAttendancePlanDao(AttendancePlanDao attendancePlanDao) {
        this.attendancePlanDao = attendancePlanDao;
    }

}